package service

import (
	"context"
	"errors"
	"github.com/spf13/viper"
	cos "github.com/tencentyun/cos-go-sdk-v5"
	sts "github.com/tencentyun/qcloud-cos-sts-sdk/go"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
)

type buckConfig struct {
	Bucket          string
	AppId           string
	SecretId        string
	SecretKey       string
	Region          string
	BucketShortName string
	Url             string
}

type GenerateTempSecretKeyRes struct {
	CR     *sts.CredentialResult `json:"cr"`
	Bucket string                `json:"bucket"`
	Region string                `json:"region"`
}

type URLToken struct {
	SessionToken string `url:"x-cos-security-token,omitempty" header:"-"`
}

// 同步到COS
func SynchronizeToCos(folder string, nameString string, pathPrefix string) (err error) {

	if len(nameString) <= 0 {
		err = errors.New("Files does not exist")
		return
	}

	// 初始化cos对象
	u, _ := url.Parse(viper.GetString("cos.url"))
	b := &cos.BaseURL{BucketURL: u}
	c := cos.NewClient(b, &http.Client{
		Transport: &cos.AuthorizationTransport{
			SecretID:  viper.GetString("cos.secret_id"),
			SecretKey: viper.GetString("cos.secret_key"),
		},
	})

	// 获取静态文件地址
	staticPath := viper.GetString("server.staticPath")

	// 获取当前文件字符串数组
	nameString = strings.TrimRight(nameString, ",")
	nameStringArr := strings.Split(nameString, ",")

	for index := range nameStringArr {
		// 通过本地文件上传对象
		basePath := staticPath + nameStringArr[index]

		_, err = c.Object.PutFromFile(context.Background(), "static/"+pathPrefix+"/"+nameStringArr[index], basePath, nil)

		if err != nil {
			continue
		}
	}

	// 无论成功与否都删除上传文件目录
	err = os.RemoveAll(staticPath + folder)

	return
}

// 获得存储配置 getBuckConfig
func getBuckConfig(buckName string) (res buckConfig, err error) {

	res.Url = viper.GetString(buckName + ".url")
	res.Bucket = viper.GetString(buckName + ".bucket")
	res.AppId = viper.GetString(buckName + ".app_id")
	res.SecretId = viper.GetString(buckName + ".secret_id")
	res.SecretKey = viper.GetString(buckName + ".secret_key")
	res.Region = viper.GetString(buckName + ".region")
	res.BucketShortName = viper.GetString(buckName + ".short_buck")
	if res.Url == "" || res.Bucket == "" || res.Region == "" || res.AppId == "" || res.SecretId == "" || res.SecretKey == "" || res.BucketShortName == "" {
		err = errors.New(buckName + " cos config not complete")
		return
	}
	return
}

// GenerateTempSecretKey 生成临时秘钥
func GenerateTempSecretKey(buckName string, PolicyStatement []sts.CredentialPolicyStatement) (res GenerateTempSecretKeyRes, err error) {

	if buckName == "" {
		buckName = "projectCos"
	}

	buckC, err := getBuckConfig(buckName)
	if err != nil {
		return
	}

	res.Bucket = buckC.Bucket
	res.Region = buckC.Region

	c := sts.NewClient(
		buckC.SecretId,
		buckC.SecretKey,
		nil,
	)

	opt := &sts.CredentialOptions{
		DurationSeconds: int64(time.Hour.Seconds()),
		Region:          buckC.Region,
		Policy:          &sts.CredentialPolicy{},
	}
	if len(PolicyStatement) > 0 {
		// 传递进来策略就使用传递的策略
		opt.Policy.Statement = PolicyStatement
	} else {
		// 没有传递策略就应用默认策略
		allowPrefix := "*"
		opt.Policy.Statement = []sts.CredentialPolicyStatement{
			{
				Action: []string{
					//简单上传操作
					"name/cos:PutObject",
					//表单上传对象
					"name/cos:PostObject",
					//分块上传：初始化分块操作
					"name/cos:InitiateMultipartUpload",
					//分块上传：List 进行中的分块上传
					"name/cos:ListMultipartUploads",
					//分块上传：List 已上传分块操作
					"name/cos:ListParts",
					//分块上传：上传分块块操作
					"name/cos:UploadPart",
					//分块上传：完成所有分块上传操作
					"name/cos:CompleteMultipartUpload",
					//取消分块上传操作
					"name/cos:AbortMultipartUpload",
					//下载权限
					"name/cos:GetObject",
					//获取文件信息
					"name/cos:HeadObject",
					"name/cos:OptionsObject",
				},
				Effect: "allow",
				Resource: []string{
					//这里改成允许的路径前缀，可以根据自己网站的用户登录态判断允许上传的具体路径，例子： a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
					//"qcs::cos:" + region + ":uid/" + appId + ":prefix//" + appId + "/" + bucket + "/*",
					"qcs::cos:" + buckC.Region + ":uid/" + buckC.AppId + ":prefix//" + buckC.AppId + "/" + buckC.BucketShortName + "/" + allowPrefix,
				},
			},
		}
	}
	res.CR, err = c.GetCredential(opt)
	return
}

// GetResourceTempUrl 通过资源key获取地址
func GetResourceTempUrl(buckName, resourceKey string) (res string, err error) {
	if buckName == "" {
		buckName = "projectCos"
	}
	bc, err := getBuckConfig(buckName)
	if err != nil {
		return
	}
	downloadPolicy := []sts.CredentialPolicyStatement{
		{
			Action: []string{
				//简单下载操作
				"name/cos:GetObject",
			},
			Effect: "allow",
			Resource: []string{
				//这里改成允许的路径前缀，可以根据自己网站的用户登录态判断允许上传的具体路径，例子： a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
				//"qcs::cos:" + region + ":uid/" + appId + ":prefix//" + appId + "/" + bucket + "/*",
				"qcs::cos:" + bc.Region + ":uid/" + bc.AppId + ":prefix//" + bc.AppId + "/" + bc.BucketShortName + "/" + resourceKey,
			},
		},
	}

	cr, err := GenerateTempSecretKey(buckName, downloadPolicy)
	if err != nil {
		return
	}
	opt := &cos.PresignedURLOptions{
		Query:  &url.Values{},
		Header: &http.Header{},
	}
	opt.Query.Add("x-cos-security-token", cr.CR.Credentials.SessionToken)
	u, _ := url.Parse(bc.Url)
	b := &cos.BaseURL{BucketURL: u}
	c := cos.NewClient(b, &http.Client{})
	presignedURL, err := c.Object.GetPresignedURL(context.Background(), http.MethodGet, resourceKey,
		cr.CR.Credentials.TmpSecretID, cr.CR.Credentials.TmpSecretKey,
		time.Hour, opt)
	if err == nil {
		res = presignedURL.String()
	}
	return
}

// GetBathResourceTempUrl 批量通过资源key获取地址
func GetBathResourceTempUrl(buckName string, resourceKey []string) (res []string, err error) {

	if buckName == "" {
		buckName = "projectCos"
	}
	bc, err := getBuckConfig(buckName)
	if err != nil {
		return
	}

	for _, resourceKeyValue := range resourceKey {

		downloadPolicy := []sts.CredentialPolicyStatement{
			{
				Action: []string{
					//简单下载操作
					"name/cos:GetObject",
				},
				Effect: "allow",
				Resource: []string{
					//这里改成允许的路径前缀，可以根据自己网站的用户登录态判断允许上传的具体路径，例子： a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
					//"qcs::cos:" + region + ":uid/" + appId + ":prefix//" + appId + "/" + bucket + "/*",
					"qcs::cos:" + bc.Region + ":uid/" + bc.AppId + ":prefix//" + bc.AppId + "/" + bc.BucketShortName + "/" + resourceKeyValue,
				},
			},
		}

		cr, _ := GenerateTempSecretKey(buckName, downloadPolicy)
		opt := &cos.PresignedURLOptions{
			Query:  &url.Values{},
			Header: &http.Header{},
		}
		opt.Query.Add("x-cos-security-token", cr.CR.Credentials.SessionToken)
		u, _ := url.Parse(bc.Url)
		b := &cos.BaseURL{BucketURL: u}
		c := cos.NewClient(b, &http.Client{})
		presignedURL, _ := c.Object.GetPresignedURL(context.Background(), http.MethodGet, resourceKeyValue,
			cr.CR.Credentials.TmpSecretID, cr.CR.Credentials.TmpSecretKey,
			time.Hour, opt)
		res = append(res, presignedURL.String())
	}
	return
}
